﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;

namespace SimpleReactiveObjects.Test.SimpleReactiveObjects
{
    [TestClass]
    public class MessengerTestScenarios
    {
        public class Mediator : IDisposable
        {
            private readonly CompositeDisposable disposables;

            public Mediator()
            {
                Running = new ReactiveVariable<bool>();
                Switch = new ReactiveMessenger<Unit>();

                var sw = Switch.Select(x => Running.Value).Publish();

                Start = sw.SelectMany(running => running  ? Observable.Empty<Unit>() : Observable.Return(Unit.Default)).ToReactiveMessenger();
                Stop  = sw.SelectMany(running => !running ? Observable.Empty<Unit>() : Observable.Return(Unit.Default)).ToReactiveMessenger();

                disposables = new CompositeDisposable()
                {
                    Running,
                    Switch,
                    Start,
                    Stop,
                    sw.Connect(),
                };
            }

            public ReactiveVariable<bool> Running { get; private set; }

            public ReactiveMessenger<Unit> Switch { get; private set; }

            public ReactiveMessenger<Unit> Start { get; private set; }

            public ReactiveMessenger<Unit> Stop { get; private set; }

            public void Dispose()
            {
                disposables.Dispose();
            }
        }

        public class Service : IDisposable
        {
            private readonly Mediator mediator;
            private readonly CompositeDisposable disposables;


            public Service(Mediator mediator)
            {
                this.mediator = mediator;
                disposables = new CompositeDisposable();

                disposables.Add(mediator.Start.Where(_ => !mediator.Running.Value).Subscribe(_ => Start()));
                disposables.Add(mediator.Stop.Where(_ => mediator.Running.Value).Subscribe(_ => Stop()));
            }

            private void Start()
            {
                Debug.Indent();
                Debug.WriteLine("Service.Start");
                Debug.Unindent();

                mediator.Running.Value = true;
            }

            private void Stop()
            {
                Debug.Indent();
                Debug.WriteLine("Service.Stop");
                Debug.Unindent();

                mediator.Running.Value = false;
            }

            public void Dispose()
            {
                disposables.Dispose();
            }
        }

        [TestMethod]
        public void TestMethod1()
        {
            using (var disposables = new CompositeDisposable())
            {
                var mediator = new Mediator();
                var service = new Service(mediator);

                disposables.Add(mediator);
                disposables.Add(service);

                Action log = () =>
                {
                    Debug.Indent();
                    Debug.WriteLine("mediator.Running = {0}", mediator.Running);
                    Debug.Unindent();
                };

                Debug.WriteLine("Initial state");
                var d = mediator.Running.Subscribe(x => log());
                disposables.Add(d);

                Debug.WriteLine("Switch state to running");
                mediator.Switch.Raise(Unit.Default);

                Debug.WriteLine("Force start (ignored)");
                mediator.Start.Raise(Unit.Default);

                Debug.WriteLine("Switch state to not running");
                mediator.Switch.Raise(Unit.Default);

                Debug.WriteLine("Force stop (ignored)");
                mediator.Stop.Raise(Unit.Default);
            }
        }
    }
}
